/* Janus Chat - Version 1.0
 * Authors: Matthew Perry
 *          Michael Freid
 *          Marc Latou
 *          Richard Lee
 *
 * The Janus Chat Client is a highly secured
 * peer-to-peer chat client designed to facilitate
 * the transfer of sensitive data or as a communication
 * medium for the extra paranoid
 *
 * This file contains the implementation for a Unix Server 
 * Socket Class wrapper. This will facilitate the production 
 * of janus' functions. */

/* Available ports are 49152 through 65535 */

#define START_PORT 49152
#include "serversocket.h"
#include "exceptions.h"
#include "unix_exceptions.h" //UNIX ONLY!!

/* The ServerSocket constructor;
 * This constructor does a few things.
 * It first creates a new socket to
 * listen with. It binds the socket to
 * a local port and then begins listening
 * for new connections */
ServerSocket::ServerSocket()
{
    //Create a new socket
    listenFD = socket( AF_INET, SOCK_STREAM, 0 );
    //Throw an exception if there was a problem
    if( listenFD < 0 ) 
        throw new SocketException( "Could not create socket" );

    //Use the internet and listen on all interfaces
    me.sin_family = AF_INET;
    me.sin_addr.s_addr = INADDR_ANY;
    //Find a port to listen on and bind to it
    findAndBind();

    //Listen on this socket to connections
    int retval = listen( listenFD, 5 );
    //Throw an exception in the event of an error
    if( retval < 0 )
        throw new SocketException( "Could not listen" );
}

/* The acceptSocket function:
 * WARNING: This function blocks execution
 * while it waits for an incoming connection
 * once it finds one, it returns a new socket */
Socket ServerSocket::acceptSocket()
{
    //Block and wait for new connections
    int peerLen = sizeof( peer );
    int newsock = accept( listenFD, (struct sockaddr *)&peer, (socklen_t *)&peerLen );
    //Throw an exception if there was a problem
    if( newsock < 0 )
        throw new SocketException( "Accepting socket failed" );

    //Return the new socket
    return Socket( newsock, peer );
}

/* The acceptSocket function with timeout:
 * WARNING: This function blocks execution
 * while it waits for an incoming connection
 * once it finds one, it returns a new socket 
 * that has a timeout parameter set  */
Socket ServerSocket::acceptSocket( struct timeval timeout ) 
{
    //Block and wait for new connections
    int peerLen = sizeof( peer );
    int newsock = accept( listenFD, (struct sockaddr *)&peer, (socklen_t *)&peerLen );
    //Throw an exception if there was a problem
    if( newsock < 0 )
        throw new SocketException( "Accepting socket failed" );

    //Return the new socket
    return Socket( newsock, peer, timeout );
}

 
/* The findAndBind function:
 * This function scans for open ports
 * in the valid unused range and binds
 * the listening socket to one */
void ServerSocket::findAndBind()
{
    //Define the starting point
    int port = START_PORT;
    int len = sizeof( me );
    int retval;
    //Loop until the port has been successfully bound
    do
    {
        //Convert the port to network endian
        me.sin_port = htons( port ); 
        //Attempt to bind
        retval = bind( listenFD, (struct sockaddr *)&me, len );
        //If it failed, increase the port and loop
        if( retval < 0 ) port++;
    }
    while( retval < 0 && port < 65535 ); //65535 is the highest available port
    //In the almost impossible chance that there are no ports,
    //Throw an exception.
    if( port == 65535 )
        throw new SocketException( "Could not bind port" );

    //Set the listening port
    listenPort = port;
}
